﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Dqaqs.Common;
using Dqaqs.Data;
using Dqaqs.Services.ServiceContracts;

namespace Dqaqs.Services
{
	public class DataSourceService : IDataSourceService
	{
		public void NewDataSource(DataSource dataSource)
		{
			if (CheckDataSourceExists(dataSource))
			{
				throw new InvalidOperationException("Similar data source already exists.");
			}
			using (var d = DataConatiner.New())
			{
				d.DataSources.AddObject(dataSource);
				d.SaveChanges();
			}
		}

		public bool CheckDataSourceExists(DataSource dataSource)
		{
			using (var d = DataConatiner.New())
			{
				if (d.DataSources.Any(ds => ds.ProjectId.Equals(dataSource.ProjectId) &&
					(ds.Id.Equals(dataSource.Id) || ds.Title.Equals(dataSource.Title))))
				{
					return true;
				}
			}
			return false;
		}

		public DataSource GetDataSource(int id)
		{
			using (var d = DataConatiner.New())
			{
				return d.DataSources.FirstOrDefault(ds => ds.Id.Equals(id));
			}
		}

		public void DeleteDataSource(int id)
		{
			using (var d = DataConatiner.New())
			{
				var dSource = d.DataSources.FirstOrDefault(ds => ds.Id.Equals(id));
				if (dSource != null)
					d.DeleteObject(dSource);
				if (dSource != null && dSource.SchemaDefinition != null && dSource.SchemaDefinition.Tables != null)
				{
					foreach (var tbl in dSource.SchemaDefinition.Tables)
					{
						UseNewConnection(d2 => d2.DropTable(dSource.ProjectId, dSource.Id, tbl.Name));
					}
				}
				d.SaveChanges();
			}
		}

		public IEnumerable<DataSource> ListDataSources(Guid projectId)
		{
			using (var d = DataConatiner.New())
			{
				return d.DataSources.Where(ds => ds.ProjectId.Equals(projectId)).ToList();
			}
		}

		public void UpdateDataSource(DataSource dataSource)
		{
			try
			{
				using (var d = DataConatiner.New())
				{
					var dSrc = d.DataSources.FirstOrDefault(p => p.Id.Equals(dataSource.Id));
					if (dSrc == null)
						throw new InvalidOperationException("Data source does not exist");
					var changedTables = Schema.CompareDefinition(dSrc.SchemaDefinition, dataSource.SchemaDefinition).ToList();
					var tablesToDrop = changedTables.Where(t => t.Type.Equals(DiffType.Modified) || t.Type.Equals(DiffType.Deleted))
						.Select(t => t.First).ToList();
					var tablesToAdd = changedTables.Where(t => t.Type != DiffType.Deleted).Select(t => t.First ?? t.Second).ToList();
					tablesToDrop.ForEach(t => UseNewConnection(d2 => d2.DropTable(dataSource.ProjectId, dataSource.Id, t)));
					tablesToAdd.ForEach(t => UseNewConnection(d2 => d2.CreateTable(dataSource.ProjectId, dataSource.Id, t, dataSource.Schema)));
					d.ApplyCurrentValues("DataSources", dataSource);
					d.SaveChanges();
				}
			}
			catch (Exception ex)
			{
				if (ex.InnerException != null)
					throw ex.InnerException;
			}
		}

		public void UploadData(UploadMessage upload)
		{
			try
			{
				ArgumentCheck.NotNull(upload.DataSourceId, "DataSourceId");
				ArgumentCheck.NotNull(upload.FileStream, "FileStream");
				ArgumentCheck.NotNullOrEmpty(upload.Table, "Table");
				var lines = upload.FileStream; //.Split("\r\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
				var header = lines.First().Split(",".ToCharArray());
				var ds = GetDataSource(upload.DataSourceId);
				if (ds.SchemaDefinition == null || ds.SchemaDefinition.Tables == null)
					throw new InvalidOperationException(String.Format("Table {0} does not exist in data source {1}.", upload.Table,
					                                                  ds.Title));
				var tbl = ds.SchemaDefinition.Tables.Where(t => t.Name == upload.Table).First();
				if (tbl == null)
					throw new InvalidOperationException(String.Format("Table {0} does not exist in data source {1}.", upload.Table,
					                                                  ds.Title));
				Crud.UploadData(header, tbl, lines, ds.ProjectId, ds.Id, upload.Table, ds.SchemaDefinition);
			}
			catch(Exception ex)
			{
				if (ex.InnerException != null)
					throw ex.InnerException;
				throw ex;
			}
		}

		private static void UseNewConnection(Action<DqaqsContainer> act)
		{
			using (var d = DataConatiner.New())
			{
				act(d);
			}
		}
	}
}
